Skip to content

fix(Android, Stack v4): fall back to rootWindowInsets for status bar top when ancestor consumed insets#3870

Closed
khorark wants to merge 1 commit intosoftware-mansion:mainfrom
khorark:fix/android-custom-toolbar-status-bar-insets
Closed

fix(Android, Stack v4): fall back to rootWindowInsets for status bar top when ancestor consumed insets#3870
khorark wants to merge 1 commit intosoftware-mansion:mainfrom
khorark:fix/android-custom-toolbar-status-bar-insets

Conversation

@khorark
Copy link
Copy Markdown

@khorark khorark commented Apr 10, 2026

Description

Fixes a regression introduced in #3240 where the native stack header title renders behind the status bar on Android 15 (SDK 35) with edge-to-edge enabled.

Root cause

PR #3240 changed CustomToolbar.onApplyWindowInsets to read systemBars insets from unhandledInsets (received from ancestor views) instead of rootWindowInsets.

When SafeAreaProvider from react-native-safe-area-context wraps the app (standard setup), it consumes the systemBars.top inset before it reaches CustomToolbar. As a result:

unhandledInsets.getInsets(Type.systemBars()).top == 0  // consumed by ancestor

So CustomToolbar computes paddingTop = 0 and the toolbar title renders at y=0, visually behind the status bar.

Before #3240, rootWindowInsets was used with ignoreVisibility = true, which always provided a stable, non-zero status bar height regardless of what ancestors consumed.

Fix

For the status bar top height specifically, fall back to rootWindowInsets when unhandledInsets.top == 0. rootWindowInsets always contains the raw window insets that no ancestor can consume:

val statusBarTop = if (shouldApplyTopInset) {
    val fromUnhandled = systemBarInsets.top
    if (fromUnhandled > 0) fromUnhandled
    else resolveInsetsOrZero(WindowInsetsCompat.Type.systemBars()).top // fallback to rootWindowInsets
} else 0

This preserves the intent of #3240 (use ancestor-received insets when available) while fixing the regression when an ancestor has already consumed the top inset.

Affected versions

  • 4.19.0 – 4.24.0 🐞
  • 4.18.0 ✅ (used rootWindowInsets, worked correctly)

Changes

  • CustomToolbar.kt: fall back to rootWindowInsets for statusBarTop when unhandledInsets.top == 0

Test code and steps to reproduce

  1. Enable edge-to-edge (edgeToEdgeEnabled=true in gradle.properties)
  2. Wrap the app with SafeAreaProvider from react-native-safe-area-context (standard setup)
  3. Push any screen with a native header using createNativeStackNavigator
  4. Run on Android 15 physical device or emulator

Expected: Header title appears below the status bar
Actual (4.19.0+): Header title renders behind/under the status bar

Environment

  • React Native: 0.85.0
  • Android: 15 (SDK 35), edgeToEdgeEnabled=true
  • Architecture: New Architecture (Fabric)
  • @react-navigation/native-stack: 7.6.1
  • react-native-safe-area-context: ^5.7.0

Checklist

  • Included code example that can be used to test this change
  • Updated TS types
  • Updated documentation

…top when ancestor consumed insets

When an ancestor view (e.g. SafeAreaProvider from react-native-safe-area-context)
consumes the systemBars top inset, unhandledInsets.top becomes 0. As a result,
CustomToolbar computes paddingTop = 0 and the header title renders behind the
status bar on Android 15 (SDK 35) with edge-to-edge enabled.

Fix: for the status bar top height specifically, fall back to rootWindowInsets
when unhandledInsets.top == 0. rootWindowInsets always contains the raw window
insets regardless of what ancestor views have consumed.

This preserves the intent of software-mansion#3240 (use ancestor-received insets when available)
while fixing the regression on setups with SafeAreaProvider wrapping the app.
Copy link
Copy Markdown
Member

@kkafar kkafar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution.

@t0maboro could I get your attention here, please? You have the most context now regarding inset handling on Android. How does the problem OP indicates relate to changes we have already made / have planned?

@khorark
Copy link
Copy Markdown
Author

khorark commented Apr 13, 2026

It was a problem with а package react-native-keyborad-controller. Last fix helped me

I think you could close this PR, thx 🙏

@t0maboro
Copy link
Copy Markdown
Contributor

Hi @khorark , thanks for the contribution!

In this PR: #3793, I added support for applying padding to the header by reading insets from the DecorView when the view is attached to the window. This retrieves the inset from ViewCompat.getRootWindowInsets https://github.com/software-mansion/react-native-screens/blob/main/android/src/main/java/com/swmansion/rnscreens/utils/DecorViewInsetsUtils.kt#L26, so this PR should be equivalent to this change.

For now, I'm going to close this PR.

@t0maboro t0maboro closed this Apr 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants